package model.simulationmodel.logicelements;

import java.util.Observable;
import java.util.Observer;

import model.editormodel.Input;
import model.editormodel.LogicEditorElement;
import model.editormodel.Output;
import model.simulationmodel.ClockHandler;
import model.simulationmodel.LogicValue;
import model.simulationmodel.SimulationModel;

/**
 * <b>Erbt:</b> <code>LogicSimulationElement</code> <br>
 * <b>Implementiert:</b> Observer <br>
 * <b>Ueberwachte Klasse: </b> <code> ClockHandler</code><br>
 * <b>Funktion:</b> Frequenzabhaengige Aenderung des ausgehenden Wertes<br>
 * <br>
 * Repraesentiert einen Taktgeber, welcher in der eingestellten Frequenz
 * abwechselnd logisch-0 (<code>L0</code>) und logisch-1 (<code>L1</code>)
 * ausgibt. Hat eine Methode zum Aendern der Frequenz.
 * 
 * @author Team Locsi
 * 
 */
public class LogicCLOCK extends LogicSimulationElement implements Observer {

	private double frequency;
	private LogicValue startValue = LogicValue.L0;

	/**
	 * Dieser Konstruktor erzeugt ein neues <code>LogicCLOCK</code>-Objekt.
	 * 
	 * @param sModel
	 *            <code>SimulationModel</code> - Referenz auf das
	 *            <code>SimulationModel</code>
	 * @param logicEditorElement
	 *            <code>LogicEditorElement</code> - Referenz auf das zugehoerige
	 *            <code>LogicEditorElement</code>
	 */
	public LogicCLOCK(SimulationModel sModel,
			LogicEditorElement logicEditorElement) {
		super(sModel, logicEditorElement);
		// standardwert fuer alle clocks
		this.frequency = 1000;
		// initial besitzt die clock den Wert L0
		this.logicInputValueMap.put(Input.STD_A, LogicValue.L0);
	}

	@Override
	public void calcLogicValue() {
		//setzt den Ausgang mit dem Wert des Eingangs
		this.setOutputValue(Output.STD_O, logicInputValueMap.get(Input.STD_A));
	}

	/**
	 * Diese Methode kehrt den eingestellten Wert um und gibt in an den Ausgang
	 * weiter, indem die calcLogicValue()-Methode aufgerufen wird.
	 */
	public void changeClockValue() {
		//logischen Wert am Eingang holen
		LogicValue a = logicInputValueMap.get(Input.STD_A);
		// falls L0 eingestellt war, wird der eingestellte Wert auf L1 gesetzt
		// falls L1 eingestellt war, wird der eingestellte Wert auf L0 gesetzt
		if (a == LogicValue.L0) {
			logicInputValueMap.put(Input.STD_A, LogicValue.L1);
		} else if (a == LogicValue.L1) {
			logicInputValueMap.put(Input.STD_A, LogicValue.L0);
		}
		calcLogicValue();
	}

	/**
	 * Prueft mit Hilfe der uebergebenen Parameter, ob eine Aenderung des
	 * logischen Wertes am Eingang noetig ist. Wenn ja, wird der Wert in der
	 * <code>changeClockValue</code> - Methode geaendert.
	 */
	@Override
	public void update(Observable arg0, Object arg1) {
		if (((Integer) arg1) >= 0) {
			if (((Integer) arg1) * ClockHandler.FREQUENCY % this.frequency == 0) {
				changeClockValue();
			}
		}
	}

	/**
	 * Diese Methode ermoeglicht das Setzen der Frequenz.
	 * 
	 * @param newFrequency
	 *            <code>double</code> - die zu setzende Frequenz
	 */
	public void setFrequency(double newFrequency) {
		if (newFrequency > 0) {
			this.frequency = newFrequency;
		}
	}

	/**
	 * Diese Methode gibt die Frequenz des Taktgebers in Hz zurueck
	 * 
	 * @return <code>double</code> -  Frequenz des Taktgebers
	 */
	public double getFrequency() {
		return (1000 / this.frequency);
	}

	/**
	 * Kehrt den AnfangsWert des Taktgebers um
	 */
	public void changeStartValue() {
		if (startValue == LogicValue.L0) {
			startValue = LogicValue.L1;
		} else if (startValue == LogicValue.L1) {
			startValue = LogicValue.L0;
		}
		logicInputValueMap.put(Input.STD_A, startValue);
	}

	/**
	 * Gibt den StartWert des Taktgebers zurueck.
	 * 
	 * @return <code>LogicValue</code> - eingestellter Startwert des Taktgebers
	 */
	public LogicValue getStartValue() {
		return startValue;
	}
}